package com.itheima.weirpc.proxy;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.itheima.weirpc.RpcApplication;
import com.itheima.weirpc.config.RpcConfig;
import com.itheima.weirpc.constant.RpcConstant;
import com.itheima.weirpc.fault.retry.RetryStrategy;
import com.itheima.weirpc.fault.retry.RetryStrategyFactory;
import com.itheima.weirpc.fault.tolerant.TolerantStrategy;
import com.itheima.weirpc.fault.tolerant.TolerantStrategyFactory;
import com.itheima.weirpc.loadbalancer.LoadBalancer;
import com.itheima.weirpc.loadbalancer.LoadBalancerFactory;
import com.itheima.weirpc.model.RpcRequest;
import com.itheima.weirpc.model.RpcResponse;
import com.itheima.weirpc.model.ServiceMetaInfo;
import com.itheima.weirpc.protocol.*;
import com.itheima.weirpc.registry.Registry;
import com.itheima.weirpc.registry.RegistryFactory;
import com.itheima.weirpc.serializer.Serializer;
import com.itheima.weirpc.serializer.SerializerFactory;
import com.itheima.weirpc.server.tcp.VertxTcpClient;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetSocket;
import io.vertx.grpc.stub.GrpcWriteStream;


import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * 服务代理（JDK 动态代理）
 *
 */
public class ServiceProxy implements InvocationHandler {

    /**
     * 调用代理
     *
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        // 指定序列化器
//        Serializer serializer = new JdkSerializer();
        // 指定序列化器
        final Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());

  String serviceName = method.getDeclaringClass().getName();

        // 构造请求
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        try {
            // 序列化
            byte[] bodyBytes = serializer.serialize(rpcRequest);

            //从注册中心获取服务提供者请求地址
            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
            serviceMetaInfo.setServiceName(serviceName);
            serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
            List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
            if(CollUtil.isEmpty(serviceMetaInfoList)){
                throw new RuntimeException("暂无服务器地址");
            }
            //暂时先取第一个 实现负载均衡后改
//            ServiceMetaInfo selectedServiceMetaInfo = serviceMetaInfoList.get(0);
//            System.out.println(selectedServiceMetaInfo.getServiceAddress());

            //负载均衡
            LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer());
            //将调用方法名作为负载均衡的参数
            Map<String,Object> requestParams = new HashMap<>();
            requestParams.put("methodName",rpcRequest.getMethodName());
            ServiceMetaInfo selectedServiceMetaInfo = loadBalancer.select(requestParams,serviceMetaInfoList);
            System.out.println("这次请求的端口是"+selectedServiceMetaInfo.getServicePort());

            //发送Tcp请求封装版
//            RpcResponse rpcResponse = VertxTcpClient.doRequest(rpcRequest,selectedServiceMetaInfo);
//            return rpcResponse.getData();
            RpcResponse rpcResponse;
            try {
                RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
                rpcResponse = retryStrategy.doRetry(()->
                        VertxTcpClient.doRequest(rpcRequest,selectedServiceMetaInfo));
            }catch (Exception e){
                //容错机制
                TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
                rpcResponse = tolerantStrategy.doTolerant(null,e);
            }

            return rpcResponse.getData();




            //发送Tcp请求
//            Vertx vertx = Vertx.vertx();
//            NetClient netClient = vertx.createNetClient();
//
//            CompletableFuture<Object> responseFuture = new CompletableFuture<>();
//
//            netClient.connect(selectedServiceMetaInfo.getServicePort(),selectedServiceMetaInfo.getServiceHost(),result->{
//                if(result.succeeded()){
//                    System.out.println("Connected to TCP server");
//                    NetSocket socket = result.result();
//
//                    //发送消息
//                    //构造消息
//                    ProtocolMessage<RpcRequest> protocolMessage = new ProtocolMessage<>();
//                    ProtocolMessage.Header header = new ProtocolMessage.Header();
//
//                    header.setMagic(ProtocolConstant.PROTOCOL_MAGIC);
//                    header.setVersion(ProtocolConstant.PROTOCOL_VERSION);
//                    header.setSerializer((byte) ProtocolMessageSerializerEnum.getEnumByValue(RpcApplication.getRpcConfig().getSerializer()).getKey());
//                    header.setType((byte) ProtocolMessageTypeEnum.REQUEST.getKey());
//                    header.setStatus((byte) ProtocolMessageStatusEnum.OK.getValue());
//                    header.setRequestId(IdUtil.getSnowflakeNextId());
//
//                    protocolMessage.setHeader(header);
//                    protocolMessage.setBody(rpcRequest);
//                    //编码请求
//                    try{
//                        Buffer encodeBuffer = ProtocolMessageEncoder.encode(protocolMessage);
//                        socket.write(encodeBuffer);
//                    } catch (IOException e) {
//                        throw new RuntimeException("协议消息编码错误");
//                    }
//                    //接收响应
//                    socket.handler(buffer -> {
//                        try {
//                            ProtocolMessage<RpcResponse> rpcResponseProtocolMessage = (ProtocolMessage<RpcResponse>) ProtocolMessageDecoder.decode(buffer);
//                            responseFuture.complete(rpcResponseProtocolMessage.getBody());
//
//                        } catch (IOException e) {
//                            throw new RuntimeException("协议消息编码错误");
//                        }
//                    });
//                }else {
//                    System.out.println("Failed to connect to Tcp server");
//                }
//            });
//
//            RpcResponse rpcResponse = (RpcResponse) responseFuture.get();
//
//            //记得关闭链接
//            netClient.close();
//            return rpcResponse.getData();

//            <-------------------------------------------------------------------------------->


//            // 发送请求
//            // todo 注意，这里地址被硬编码了（需要使用注册中心和服务发现机制解决）
//            try (HttpResponse httpResponse = HttpRequest.post(selectedServiceMetaInfo.getServiceAddress())
//                    .body(bodyBytes)
//                    .execute()) {
//                byte[] result = httpResponse.bodyBytes();
//                // 反序列化
//                RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class);
//                return rpcResponse.getData();
//            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}
